#! /usr/bin/env python
# -*- coding: utf-8 -*-
 
import tkinter as tk
from tkinter import scrolledtext
from tkinter import ttk
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog

import serial.tools.list_ports
import threading
import time
import os

class GUI:
    def __init__(self):
        self.Information_Window = scrolledtext
        self.Receive_Window = scrolledtext
        self.entryStr = Entry

        self.serialPort = serial.Serial()

        self.gui_width = 780
        self.gui_height = 530
        #定义父容器
        self.root = tk.Tk()             # 父容器
        self.root.title("Serial Tool")  # 父容器标题
        # GUI.geometry("800x550")   # 父容器大小
        self.root.geometry(
            f"{str(self.gui_width)}x{str(self.gui_height)}+"+
            f"{int((self.root.winfo_screenwidth() - self.gui_width) / 2)}+"+
            f"{int((self.root.winfo_screenheight() - self.gui_height) / 2)}"
        )
        self.root.resizable(False, False)

        self.interface()

    # 获取当前目录下的文件作为默认文件
    def getDefaultFileName(self):
        base_dir = os.getcwd()

        # 获取当前目录下的所有文件
        # files = [os.path.join(base_dir, file) for file in os.listdir(base_dir)]
        files = os.listdir(base_dir)

        for file in files:
            if os.path.splitext(file)[1] == ".md":
                return file
        return
    
    def selectUpdateFile(self):
        selectFile = filedialog.askopenfilename(
            initialdir="./",  # 设置初始目录
            title="选择文件",  # 设置对话框标题
            filetypes=(("文件", "*.md"), ("所有文件", "*.*"))  # 设置文件类型过滤
        )
        self.select_path.set(selectFile)

    def interface(self):
        #接收文本框（带滚动条）
        Receive = tk.LabelFrame(self.root, text="接收数据", padx=10, pady=10)  # 水平，垂直方向上的边距均为 10
        Receive.place(x=10, y=10)
        self.Receive_Window = scrolledtext.ScrolledText(Receive, width=100, height=20, padx=10, pady=10, wrap=tk.WORD)
        self.Receive_Window.grid()

        #显示操作信息的框图
        Information = tk.LabelFrame(self.root, text="发送信息", padx=10, pady=10)  # 创建一个框架
        Information.place(x=10, y=340)                 #将该框架放在x=10，y=340的地方
        self.Information_Window = scrolledtext.ScrolledText(Information, width=60, height=8, padx=10, pady=10, wrap=tk.WORD)         #创建一个有滚动条的文本框
        self.Information_Window.grid()
        
        start_x = 520
        start_y = 350
        tk.Label(self.root, text='data:').place(x=start_x, y=start_y)
        self.entryStr = tk.StringVar()
        self.entryStr.set("123456")
        tk.Entry(self.root, textvariable=self.entryStr).place(x=start_x + 40, y=start_y)
        tk.Button(self.root, text='send',command=self.send,width=5,height=1).place(x=start_x+195, y=start_y-5)
        
        defFile = self.getDefaultFileName()
        self.select_path = tk.StringVar(value=defFile)
        tk.Label(self.root, text="文件: ").place(x=520, y=380)
        tk.Entry(self.root, textvariable = self.select_path).place(x=560, y=380)
        tk.Button(self.root, text="选择", command=self.selectUpdateFile,width=5).place(x=715, y=375) 

        tk.Button(self.root, text='button1',command=self.pressButton_1,width=10,height=1).place(x=start_x+20, y=start_y+60)
        tk.Button(self.root, text='button2',command=self.pressButton_2,width=10,height=1).place(x=start_x+120, y=start_y+60)

        serial_start_x = 520
        serial_start_y = 450
        # 串口号
        tk.Label(self.root, text='串口号:').place(x=serial_start_x, y=serial_start_y)
        #建立端口号列表
        self.port_list = ttk.Combobox(self.root, width=12,state='readonly') #创建下拉菜单栏
        ListPorts = list(serial.tools.list_ports.comports())#扫描当前可用串口保存到表ListPorts
        self.port_list['values'] = [i[0] for i in ListPorts]## 下拉列表的值为ListPorts的所有值
        if len(ListPorts) > 0:
            self.port_list.current(0)# 初始显示表中第一个值
        else:
            self.port_list.current()# 初始显示表中第一个值
        self.port_list.place(x=serial_start_x+60, y=serial_start_y)

        # 波特率
        tk.Label(self.root, text='波特率:').place(x=serial_start_x, y=serial_start_y+25)
        self.baud_list = ttk.Combobox(self.root, width=12, state='readonly')
        self.baud_list['values'] = (115200, 9600)
        self.baud_list.current(0)# 初始显示115200
        self.baud_list.place(x=serial_start_x+60,y=serial_start_y+25)  # 设置其在界面中出现的位置  column代表列   row 代表行
        
        self.srial_butt = tk.Button(self.root, text='打开串口',background='lightgray',command=self.open_serial,width=7,height=1,state='normal')
        self.srial_butt.place(x=serial_start_x+180, y=serial_start_y+20)

    #打开串口
    def open_serial(self):
        if not self.serialPort.isOpen():
            # print("串口为关闭状态")
            self.serialPort.port = self.port_list.get()# 读取端口下拉菜单栏的选择，将其设为串口的端口
            self.serialPort.baudrate = self.baud_list.get()# 读取波特率下拉菜单栏的选择，将其设为串口的波特率
            self.serialPort.timeout = 0.5
            try:
                self.serialPort.open()   # 打开串口
            except Exception as e:
                messagebox.showerror('error','串口打开失败！')

            if self.serialPort.isOpen():
                print("打开串口成功")
                self.srial_butt['text']="关闭串口"
                self.srial_butt['background'] = 'lightgreen'
                self.srial_butt['command'] = self.close_serial
            
                t = threading.Thread(target=self.serial_rev, daemon = True) # 新建线程用来不断接收数据并显示，与主线程并行
                t.start()# 开始线程
            
    #关闭数据的读取
    def close_serial(self):
        self.serialPort.close()
        if self.serialPort.isOpen():
            print("关闭失败")
        else:
            print("关闭成功")
            self.srial_butt['text']="打开串口"
            self.srial_butt['command'] = self.open_serial
            self.srial_butt['background'] = 'lightgray'

    #不断读取串口数据，并计算
    def serial_rev(self):
        while self.serialPort.isOpen():
            data = self.serialPort.read_all()

            if data != b'' :
                time.sleep(0.02)
                data = data + self.serialPort.read_all() #延时之后再获取一遍串口接收到的数据

                self.Receive_Window.insert("end", "原始数据："+str(data) + '\n') #将接收到的原始数据打印出来
                self.Receive_Window.see("end")
                
                dataBuf = bytes.fromhex(data.hex()) # 将接收到的数据转换成数组
                print(dataBuf)
                # print("rx data len:",len(dataBuf))
                # print("hex形式输出")
                self.Receive_Window.insert("end", "转换成HEX："+str(dataBuf.hex(" ")) + '\n') #将接收到的原始数据打印出来
                self.Receive_Window.see("end")
            time.sleep(0.1)

    def pressButton_1(self):
        print("按下按钮1")
        self.Information_Window.insert("end", '按下按钮1\n')       # 在操作信息窗口显示发送的指令并换行，end为在窗口末尾处显示
        self.Information_Window.see("end")         # 此处为显示操作信息窗口进度条末尾内容，以上两行可实现窗口内容满时，进度条自动下滚并在最下方显示新的内容

        filename = self.select_path.get()

        self.Receive_Window.insert("end", 'file name:'+filename+'\n')       # 在操作信息窗口显示发送的指令并换行，end为在窗口末尾处显示
        self.Receive_Window.see("end")         # 此处为显示操作信息窗口进度条末尾内容，以上两行可实现窗口内容满时，进度条自动下滚并在最下方显示新的内容

    def pressButton_2(self):
        print('按下按钮2')
        messagebox.showwarning('提示', '按下按钮2')

    def send(self):
        str = self.entryStr.get()
        if str == '':
                messagebox.showerror('warring', '请输入有效内容')
        else: 
            print('putin:', str)

            self.Information_Window.insert("end", 'send data:'+str+'\n')       # 在操作信息窗口显示发送的指令并换行，end为在窗口末尾处显示
            self.Information_Window.see("end")         # 此处为显示操作信息窗口进度条末尾内容，以上两行可实现窗口内容满时，进度条自动下滚并在最下方显示新的内容
                   
            self.serialPort.write(str.encode('utf-8'))

            # sendData = [0x55,0xAA,0x14,0x01,0x00,0x02,0x00,0x09,0x00,0x00,0x00,0x00]
            # self.serialPort.write(bytes(sendData))

if __name__ == '__main__':
    gui = GUI()
    gui.root.mainloop()

